#include <bits/stdc++.h>
#define int long long
#define ls id << 1     // 左子节点索引
#define rs id << 1 | 1 // 右子节点索引
using namespace std;

const int N = 2e5 + 5, inf = 1e16; // 最大列数N，无穷大值inf
char c[4][N];                      // 网格数据，c[i][j]表示第i行第j列的字符

// 线段树节点结构体，存储路径信息
struct Node {
  int t[4][4]; // t[i][j] -> 从i行移动到j行的最小花费

  // 默认构造函数，初始化所有路径为无穷大
  Node() {
    for (int i = 1; i < 4; i++)
      for (int j = 1; j < 4; j++)
        t[i][j] = inf;
  }

  // 构造函数：根据列id构建单列的路径信息
  Node(int id) {
    Node(); // 先调用默认构造函数初始化

    // 初始化对角线：同一行的移动代价
    for (int i = 1; i < 4; i++)
      t[i][i] = (c[i][id] == '.'
                     ? 0
                     : inf); // 如果是空地，代价为0；如果是墙，代价为无穷大

    // 计算同一列内不同行之间的移动代价
    for (int i = 1; i < 4; i++)
      for (int j = 1; j < 4; j++) {
        if (i == j)
          continue; // 跳过同一行

        if (i < j) {
          // 从第i行到第j行：通过中间行逐步移动
          // 例如：从第1行到第3行 = 从第1行到第2行 + 从第2行到第3行 + 移动步数
          t[i][j] = min(inf, t[i][j - 1] + t[j][j] + 1);
        } else {
          // 对称性：从第i行到第j行与从第j行到第i行代价相同
          t[i][j] = t[j][i];
        }
      }
  }

  // 重载+运算符：合并两个相邻列的路径信息
  Node operator+(Node &r) {
    Node s;

    // 合并策略：从第i行到第j行
    // 先从左列的第i行移动到第k行，然后向右移动一步，再从右列的第k行移动到第j行
    for (int i = 1; i < 4; i++)
      for (int k = 1; k < 4; k++)
        for (int j = 1; j < 4; j++)
          s.t[i][j] = min(s.t[i][j], t[i][k] + r.t[k][j] + 1);

    return s;
  }
} seg[N << 2]; // 线段树数组，大小为4N


/*
动态规划思路注释：
t[i][j] -> 在特定列范围内，从第i行移动到第j行的最小步数

合并策略：对于相邻的两列L和R
s.t[i][j] = minₖ { L.t[i][k] + R.t[k][j] + 1 }
含义：从左列的第i行到右列的第j行，可以选择在第k行进行中转

线段树作用：将整个网格划分为列区间，通过线段树高效合并区间信息
*/


// 构建线段树
void build(int id, int l, int r) {
  if (l == r) {        // 叶子节点：单列
    seg[id] = Node(l); // 构建单列的路径信息
    return;
  }
  int d = (l + r) >> 1;        // 中点
  build(ls, l, d);             // 递归构建左子树
  build(rs, d + 1, r);         // 递归构建右子树
  seg[id] = seg[ls] + seg[rs]; // 合并左右子树信息
}

// 更新线段树：当某个单元格状态改变时调用
void change(int id, int l, int r, int x) {
  if (l == r) {        // 找到目标列
    seg[id] = Node(l); // 重新构建该列的路径信息
    return;
  }
  int d = (l + r) >> 1; // 中点
  if (d < x)
    change(rs, d + 1, r, x); // 目标列在右子树
  else
    change(ls, l, d, x);       // 目标列在左子树
  seg[id] = seg[ls] + seg[rs]; // 更新当前节点信息
}


signed main() {
  int n, q;
  cin >> n; // 输入列数

  // 读取网格数据
  for (int i = 1; i <= 3; i++)
    for (int j = 1; j <= n; j++)
      cin >> c[i][j];

  build(1, 1, n); // 构建线段树

  cin >> q; // 输入查询次数
  while (q--) {
    int x, y;
    cin >> x >> y; // 输入要翻转的单元格坐标

    // 翻转单元格状态：墙变空地，空地变墙
    c[x][y] = (c[x][y] == '.' ? '#' : '.');

    // 更新线段树中对应列的信息
    change(1, 1, n, y);

    // 输出结果：从第1行到第3行的最小移动次数
    // 如果不可达（代价≥inf），输出-1；否则输出实际代价
    cout << (seg[1].t[1][3] >= inf ? -1 : seg[1].t[1][3]) << '\n';
  }
  return 0;
}